﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Affinity.Flux
{
    /// <summary>
    /// Provides relevant information regarding a type.
    /// </summary>
    internal class TypeInfo
    {
        public static TypeInfo Create(Type type)
        {
            List<InletDefinition> inletDefinitions = new List<InletDefinition>();
            List<OutletDefinition> outletDefinitions = new List<OutletDefinition>();
            List<ChildReferenceDefinition> childReferenceDefinitions = new List<ChildReferenceDefinition>();
         
            foreach (var member in GetTypeMembers(type))
            {
                var attributes = member.GetCustomAttributes(false);
                
                foreach (var inletAttribute in attributes.OfType<InletAttribute>())
                {
                    inletDefinitions.Add(new InletDefinition(member, Contract.Create(inletAttribute), inletAttribute.Flags));
                }

                foreach (var outletAttribute in attributes.OfType<OutletAttribute>())
                {
                    outletDefinitions.Add(new OutletDefinition(member, Contract.Create(outletAttribute), outletAttribute.Flags));
                }

                if (attributes.OfType<ComponentAttribute>().Any())
                {
                    childReferenceDefinitions.Add(new ChildReferenceDefinition((PropertyInfo)member));
                }
            }

            return new TypeInfo
            {
                _Type = type,
                _InletDefinitions = inletDefinitions.ToArray(),
                _OutletDefinitions = outletDefinitions.ToArray(),
                _ChildReferenceDefinitions = childReferenceDefinitions.ToArray()
            };

            /*
            return new TypeInfo
            {
                _Type = type,

                _InletDefinitions = GetTypeMembers(type).SelectMany(
                    member => member.GetCustomAttributes(typeof(InAttribute), false).Cast<InAttribute>().Select(
                        attribute => new InletDefinition(member, Contract.Create(attribute), attribute.Flags))).ToArray(),

                _OutletDefinitions = GetTypeMembers(type).SelectMany(
                    member => member.GetCustomAttributes(typeof(OutAttribute), false).Cast<OutAttribute>().Select(
                        attribute => new OutletDefinition(member, Contract.Create(attribute), attribute.Flags))).ToArray(),

                _ChildReferenceDefinitions = GetTypeMembers(type).SelectMany(
                    member => member.GetCustomAttributes(typeof(AutoRegisterAttribute), false).Cast<AutoRegisterAttribute>().Select(
                        attribute => new ChildReferenceDefinition((PropertyInfo)member))).ToArray()
            };
            */
        }

        /// <summary>
        /// Gets all the relevant members of a specified type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static IEnumerable<MemberInfo> GetTypeMembers(Type type)
        {
            return type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
        }

        /// <summary>
        /// Private constructor.
        /// </summary>
        private TypeInfo()
        {
        }

        /// <summary>
        /// Gets the child reference definitions.
        /// </summary>
        public ChildReferenceDefinition[] ChildReferenceDefinitions
        {
            get { return _ChildReferenceDefinitions; }
        }

        /// <summary>
        /// Gets the inlet definitions.
        /// </summary>
        public InletDefinition[] InletDefinitions
        {
            get { return _InletDefinitions; }
        }

        /// <summary>
        /// Gets the outlet definitions.
        /// </summary>
        public OutletDefinition[] OutletDefinitions
        {
            get { return _OutletDefinitions; }
        }

        /// <summary>
        /// Gets the Type this TypeInfo represents.
        /// </summary>
        public Type Type
        {
            get { return _Type; }
        }

        private ChildReferenceDefinition[] _ChildReferenceDefinitions;
        private InletDefinition[] _InletDefinitions;
        private OutletDefinition[] _OutletDefinitions;
        private Type _Type;
    }
}
